package com.rran.study.designpattern.behavioral.chain.chain.spring;


import com.rran.study.designpattern.behavioral.chain.annotation.HandlerChain;
import com.rran.study.designpattern.behavioral.chain.enums.ChainAction;
import com.rran.study.designpattern.behavioral.chain.enums.ChainType;
import com.rran.study.designpattern.behavioral.chain.enums.HandleResult;
import com.rran.study.designpattern.behavioral.chain.factory.ChainHandlerFactory;
import com.rran.study.designpattern.behavioral.chain.repository.ChainHandlerRepository;
import com.rran.study.designpattern.behavioral.chain.model.ChainContext;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * @author yy
 * @Type AbstractSpringChain.java
 * @Desc
 * @date 2020/7/20 14:44
 */
public abstract class AbstractSpringChain implements ChainSpringHandler, ApplicationContextAware, InitializingBean {

    protected ApplicationContext applicationContext;
    protected ChainSpringHandler nextHandler;
    protected ChainAction action;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public ChainAction getAction() {
        return this.action;
    }

    @Override
    public ChainSpringHandler getNextHandler() {
        return nextHandler;
    }

    @Override
    public void handle(ChainContext context) {
        HandlerChain handlerChain = getClass().getAnnotation(HandlerChain.class);
        String action = handlerChain.actionName();
        HandleResult handleResult = HandleResult.parse(context.getHandleResult());
        if (canHandle(context, handlerChain)) {
            handleResult = doHandle(context);
            context.setAction(action);
            if (HandleResult.FAIL == handleResult) {
                //直接进入失败的终态
                doFail(context);
            }
            context.setHandleResult(handleResult.getValue());
            if (HandleResult.STOP == handleResult) {
                //不需要继续往下运行
                System.out.println("责任链执行完成");
            }
        }

        if (nextHandler != null) {
            if (HandleResult.COMPLETE == handleResult) {
                doComplete(context);
            } else {
                nextHandler.handle(context);
            }
        } else {
            //最后一步处理完成
            doComplete(context);
        }
        //完成
        doHandleComplete(context);
    }

    private boolean canHandle(ChainContext context, HandlerChain handlerChain) {
        ChainType chainType = handlerChain.chainType();
        ChainAction contextAction = ChainHandlerFactory.getChainAction(chainType, context.getAction());
        ChainAction targetAction = ChainHandlerFactory.getChainAction(chainType, handlerChain.actionName());
        if (targetAction == null || contextAction == null) {
            throw new RuntimeException("targetAction and contextAction must not be empty");
        }
        //兼容同一层级不同场景先后执行的情况
        return targetAction.getValue() >= contextAction.getValue();
    }


    @Override
    public void afterPropertiesSet() {
        HandlerChain handlerChain = getClass().getAnnotation(HandlerChain.class);
        if (handlerChain == null) {
            throw new RuntimeException("handler must be with annotation HandlerChain");
        }
        ChainType chainType = handlerChain.chainType();
        //将每个责任链的头放入map中
        if (handlerChain.isHead()) {
            ChainHandlerRepository.storeHeadHandler(chainType, this);
        }
        ChainAction action = chainType.getAction();
        Class<? extends ChainAction> clazz = action.getClass();
        ChainAction[] actions = clazz.getEnumConstants();
        for (ChainAction chainAction : actions) {
            //设置当前handler的action
            if (chainAction.getName().equals(handlerChain.actionName())) {
                this.action = chainAction;
            }
            ChainHandlerRepository.storeAction(chainType, chainAction);
        }
        //下一个链
        String nextHandlerName = handlerChain.nextHandler();
        if (StringUtils.isNotBlank(nextHandlerName)) {
            this.nextHandler = applicationContext.getBean(nextHandlerName, ChainSpringHandler.class);
        }
        ChainHandlerRepository.storeChain(chainType, this);
    }


    public abstract void doHandleComplete(ChainContext context);

    public abstract HandleResult doHandle(ChainContext context);

    public abstract void doFail(ChainContext context);

    protected abstract void doComplete(ChainContext context);


}
